Java Virtual Machine (JVM) implementations support the Java Native Interface (JNI) as a mechanism which allows Java code to call methods written in C and C++ (native code) and vice verse. Traditionally both the code written in Java and the native code is executed in the same process and by the same thread as execution transitions between the two.
It is possible, however, to construct a JVM such that the native code is run in one or more Remote Execution Containers which may be hosted in separate processes on the same or different machines from where the Java code is executed such that the native code is unaware that it is executing separately from the JVM. This separation prevents misbehaved native code from destabilizing the JVM and enables running the native code in a different environment (e.g., security context, bit width) than the main JVM.
In a split JVM, the cost of the calls between Java and native code has much greater overhead and latency resulting in the need to reduce round-trips where possible. With the standardized Java Native Interface (JNI), an application often has to make multiple calls to get the information needed to complete an action. In a traditional JVM, the overhead for a JNI to Java call is low enough to be acceptable. In the case of a distributed JVM, however, the latency of making a cross-process/cross-machine call may be magnitudes of order greater than required to run the method called. The JNI calls that are of particular focus are the ones associated with native buffers.
In one example use case, JVM Proxy technology can be used to realize the true potential of hybrid systems with transparent acceleration of Java workload running on a main machine (host) by running Java methods on another machine (accelerator) while leaving required native methods run on the main machine. When a native buffer is accessed from both Java and native methods, accessing the buffer from remote machine will cause a network round-trip overhead.
For instance, ByteBuffer instances, created by a call to static ByteBuffer java.nio.ByteBuffer.allocateDirect(int nbyte), create regions of non-heap memory which can be shared between native and managed code. Briefly, ByteBuffer is a Java™ class that defines input/output (I/O) operations upon byte buffers. Native code refers to code written in machine language or the like that can be executed on a native machine. Managed code refers to code written in language that requires another program to run it. Typically single address is used for the buffer in native and managed space: JVM on a single machine. In the hybrid environment, Java and native (JNI) code run on different machines. For example, native code running on the host machine could allocate a virtual address. Now the managed code on the accelerator machine will have to reach out to the host machine for two kinds of operations: to read and write the data at that address and to respond to address look up requests. This naïve allocation of direct byte buffers on the JNI-side (host-side) makes accesses expensive for some cases. Excessive roundtrips for data make this approach non-usable for real applications in which slowdowns have been observed in excesses of 50 times.